import { BaseObject } from "./object";
import { Event, EventDispatcher } from "./event";
// 
export type NotificationType = number;
export type Notification = { type: NotificationType, data: any, target: Model | View<any, any> | null };
type ViewListener = (notification: Notification) => void;
type CommandCallback<T extends Command<any, any>> = (command: T) => void;
type CommandBin<T extends Command<any, any>> = { commandClass: { new (): T }, guard: CommandCallback<T> | null, hook: CommandCallback<T> | null };
/**
 * 根据数据查找视图。
 */
export function getViewByModel<M extends Model, V extends View<M, any>>(model: M, views: V[]): V | null {
    for (const view of views) {
        if (view.model === model) {
            return view;
        }
    }

    return null;
}
/**
 * 数据。
 */
export abstract class Model extends BaseObject {
    /**
     * 发送通知。
     */
    protected _sendNotification(type: NotificationType, data: any): void {
        context.sendNotification(type, data, this);
    }

    protected _onClear(): void {
    }
}
/**
 * 视图。
 */
export abstract class View<M extends Model, Component> extends EventDispatcher {
    /**
     * 数据。
     */
    public model: M;
    /**
     * 组件。
     */
    public component: Component;
    /**
     * 转发通知。
     */
    private _notificationHandler(event: Event, listener: ViewListener): void {
        listener.call(this, event.data);
    }
    /**
     * 初始化。
     */
    protected _initialize(): void {
    }
    /**
     * 侦听通知。
     */
    protected _addNotification(type: NotificationType, listener: ViewListener): void {
        context.addEventListener(String(type), this._notificationHandler, this, listener);
    }
    /**
     * 取消侦听通知。
     */
    protected _removeNotification(type: NotificationType, listener: ViewListener): void {
        context.removeEventListener(String(type), this._notificationHandler, listener);
    }
    /**
     * 发送通知。
     */
    protected _sendNotification(type: NotificationType, data: any): void {
        context.sendNotification(type, data, this);
    }
    /**
     * 初始化。
     */
    public initialize(model: M, component: Component): void {
        this.model = model;
        this.component = component;
        this._initialize();
        this.update();
    }
    /**
     * 更新视图。
     */
    public update(): void {
    }
}
/**
 * 命令。
 */
export class Command<D, R> extends BaseObject {
    public static toString(): string {
        return "[class Command]";
    }
    /**
     * 发起命令的通知。
     */
    public notification: Notification;
    /**
     * 命令的数据输出。
     */
    public result: R | null = null;
    /**
     * 是否被持有。（异步命令防止被垃圾回收）
     */
    private _isRetained: boolean = false;
    /**
     * 命令的所属队列。（异步命令需要异步通知队列继续，在释放调用中通知队列）
     */
    public _queue: QueueCommand<any, any> | null = null;

    protected _onClear(): void {
    }
    /**
     * 发送通知。
     */
    protected _sendNotification(type: number, data: any): void {
        context.sendNotification(type, data, null);
    }
    /**
     * 持有。
     */
    protected _retain(): void {
        this._isRetained = true;
        controller.retain(this);
    }
    /**
     * 释放。
     */
    protected _release(isSuccessed: boolean): void {
        if (this._queue) {
            if (isSuccessed) {
                this._queue._next(this.notification.data);
            }
        }

        this._isRetained = false;
        controller.release(this);
    }
    /**
     * 执行命令。
     */
    public execute(): void {
    }
    /**
     * 是否被持有。（异步命令防止被垃圾回收）
     */
    public get isRetained(): boolean {
        return this._isRetained;
    }
    /**
     * 命令的数据输入。
     */
    public get data(): D | null {
        return this.notification.data;
    }
}
/**
 * 命令队列。
 */
export abstract class QueueCommand<D, R> extends Command<D, R> {
    public static toString(): string {
        return "[class QueueCommand]";
    }
    /**
     * 存储每个命令的初始化数据。
     */
    private readonly _commandBins: CommandBin<Command<any, any>>[] = [];
    /**
     * 当前命令。
     */
    private _currentCommand: Command<any, any>;
    /**
     * 初始化命令队列。
     */
    protected abstract _initialize(): void;
    /**
     * 添加命令。
     */
    protected _addCommand<T extends Command<any, any>>(
        commandClass: { new (): T },
        guard: CommandCallback<T> | null = null,
        hook: CommandCallback<T> | null = null
    ): void {
        const commandBin: CommandBin<T> = { commandClass: commandClass, guard: guard, hook: hook };
        this._commandBins.push(commandBin);
    }
    /**
     * 执行命令队列。
     */
    public execute(): void {
        this._initialize();
        this._next(this.notification.data);
    }
    /**
     * 下一个命令。
     */
    public _next(data: any): void {
        if (this._commandBins.length > 0) {
            const commandBin = this._commandBins.shift();
            if (commandBin) {
                this._currentCommand = controller.create(commandBin);
                this._currentCommand._queue = this;
                controller.execute(this._currentCommand, { type: 0, data: data, target: null }, commandBin); //
                if (!this._currentCommand.isRetained) {
                    this._next(this._currentCommand.result);
                }
            }
        }
    }
}
/**
 * 上下文。
 */
export class Context extends EventDispatcher {
    public static toString(): string {
        return "[class Context]";
    }
    /**
     * 判断是否注册命令（侦听器）的依据。
     */
    protected _isSameListener(a: any, b: any): boolean {
        if (a && (a as CommandBin<Command<any, any>>).commandClass && b) {
            return (a as CommandBin<Command<any, any>>).commandClass === b;
        }

        return super._isSameListener(a, b);
    }
    /**
     * 发送通知。
     */
    public sendNotification(type: NotificationType, data: any, target: any = null): void {
        console.log("sendNotification", type, data, target);

        this.dispatchEventWidth(String(type), { type: type, data: data, target: target });
    }
}
/**
 * 控制器。
 */
export class Controller extends BaseObject {
    public static toString(): string {
        return "[class Controller]";
    }
    /**
     * 被持有的命令。（防止垃圾回收）
     */
    private readonly _commands: Command<any, any>[] = [];
    /**
     * 收到通知并执行命令。
     */
    private _notificationHandler(
        event: Event,
        commandBin: CommandBin<Command<any, any>>
    ): void {
        const command = this.create(commandBin);
        this.execute(command, event.data, commandBin);
    }

    protected _onClear(): void {
    }
    /**
     * 注册命令。
     */
    public register<T extends Command<any, any>>(
        type: NotificationType,
        commandClass: { new (): T },
        guard: CommandCallback<T> | null = null,
        hook: CommandCallback<T> | null = null
    ): void {
        const commandBin: CommandBin<T> = { commandClass: commandClass, guard: guard, hook: hook };
        context.addEventListener(String(type), this._notificationHandler, this, commandBin);
    }
    /**
     * 取消注册的命令。
     */
    public unregister<T extends Command<any, any>>(
        type: NotificationType,
        command: { new (): T }
    ): void {
        context.removeEventListener(String(type), this._notificationHandler, command);
    }
    /**
     * 生成一个命令。
     */
    public create<T extends Command<any, any>>(
        commandBin: CommandBin<T>
    ): T {
        const command = Command.create(commandBin.commandClass);
        return command;
    }
    /**
     * 执行命令。
     */
    public execute<T extends Command<any, any>>(
        command: T,
        notifycation: Notification,
        commandBin: CommandBin<T>
    ): void {
        command.notification = notifycation;

        if (commandBin.guard !== null) {
            commandBin.guard(command);
        }

        command.execute();

        if (commandBin.hook !== null) {
            commandBin.hook(command);
        }
    }
    /**
     * 持有命令。（防止被垃圾回收）
     */
    public retain(command: Command<any, any>): void {
        if (this._commands.indexOf(command) < 0) {
            this._commands.push(command);
        }
    }
    /**
     * 释放命令。
     */
    public release(command: Command<any, any>): void {
        const index = this._commands.indexOf(command);
        if (index >= 0) {
            this._commands.splice(index, 1);
        }
    }
}
/**
 * 上下文实例。
 */
export const context: Context = Context.getInstance(Context);
/**
 * 控制器实例。
 */
export const controller: Controller = Controller.getInstance(Controller);;